package org.teremail.mailbox;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.teremail.mailbox.Path.path;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.sql.DataSource;

import org.junit.Before;
import org.junit.Test;
import org.teremail.AllTests;
import org.teremail.DBConfig;
import org.teremail.message.Content;
import org.teremail.message.Entity;
import org.teremail.message.Message;
import org.teremail.message.builder.SimpleMessageBuilder;


public abstract class AbstractMailboxDAOTestCase {

    DBMailboxDAO dao = null;
    DBConfig dbConfig = null;
    
    @Before
    public void setup() throws SQLException {
        dbConfig = getDBConfig();
        dao = new DBMailboxDAO(dbConfig.getDataSource(), dbConfig.getDialect());
        dao.init();
        clear(dao.getDataSource());
    }
    
    private void clear(DataSource dataSource) throws SQLException {
        Connection cn = dataSource.getConnection();
        Statement st = cn.createStatement();
        st.execute("DELETE FROM mailbox");
        st.execute("DELETE FROM folder");
    }

    protected abstract DBConfig getDBConfig();
    
    @Test
    public void saveThenFindMailbox() throws MailboxExistsException, 
            MailboxNotExistsException {
        
        MailboxDO mdo0 = dao.create("bart");
        MailboxDO mdo1 = dao.findMailbox(mdo0.getUser());
        
        assertNotNull(mdo1);
        assertEquals("bart", mdo1.getUser());
    }

    @Test(expected=MailboxExistsException.class)
    public void duplicateMailbox() throws MailboxExistsException, 
            MailboxNotExistsException {
        dao.create("bart");
        dao.create("bart");
    }
    
    @Test(expected=MailboxNotExistsException.class)
    public void missingMailbox() throws MailboxNotExistsException {
        dao.findMailbox("foo");
    }
    
    
    @Test
    public void saveThenFindFolder() throws Exception {
        MailboxDO mdo = dao.create("bart");
        FolderDO fdo0 = new FolderDO(path("INBOX"), mdo);
        dao.save(fdo0);
        
        FolderDO fdo1 = dao.findFolder(mdo, path("INBOX"));
        assertNotNull(fdo1);
        assertEquals("bart", fdo1.getMailbox().getUser());
        assertEquals("INBOX", fdo1.getName());
    }
    
    @Test(expected=FolderExistsException.class)
    public void duplicateFolder() throws Exception {
        MailboxDO mdo = dao.create("bart");
        FolderDO fdo0 = new FolderDO(path("INBOX"), mdo);
        dao.save(fdo0);
        FolderDO fdo1 = new FolderDO(path("INBOX"), mdo);
        dao.save(fdo1);
        
    }

    @Test(expected=FolderNotExistsException.class)
    public void missingFolder() throws Exception {
        MailboxDO mdo = dao.create("bart");
        dao.findFolder(mdo, path("thisDoesNotExist"));
    }
    
    @Test
    public void addMessage() throws Exception {
        String messageId = "42";
        MailboxDO mdo = dao.create("bart");
        FolderDO fdo = dao.save(new FolderDO(path("INBOX"), mdo));
        dao.addMessage(fdo, messageId);
        
        List<Long> uids = dao.getUids(fdo);
        
        assertEquals(1, uids.size());
        assertEquals(1, (long) uids.get(0));
    }
    

    @Test
    public void getFolders() throws Exception {
        MailboxDO mdo = dao.create("bart");
        dao.save(new FolderDO(path("INBOX"), mdo));
        dao.save(new FolderDO(path("Trash"), mdo));
        dao.save(new FolderDO(path("Drafts"), mdo));
        dao.save(new FolderDO(path("Sent"), mdo));
        
        List<FolderDO> folders = dao.getFolders(mdo, Path.ROOT);
        assertEquals(4, folders.size());
        Set<String> names = new HashSet<String>();
        for (FolderDO folder : folders) {
            names.add(folder.getName());
        }
        assertTrue(names.containsAll(Arrays.asList("INBOX", "Trash", "Drafts", "Sent")));
    }
    
    @Test
    public void getEntriesSortedByUID() throws Exception {
        MailboxDO mdo = dao.create("bart");
        FolderDO fdo = new FolderDO(path("INBOX"), mdo);
        fdo = dao.save(fdo);
        
        DBMessageDAO mesDAO = new DBMessageDAO(dbConfig.getDataSource(), 
                dbConfig.getDialect());
        mesDAO.init();
        
        Message m = AllTests.getSimpleMessage();
        for (int i = 0; i < 20; i++) {
            String id = mesDAO.save(m).getId();
            dao.addMessage(fdo, id);
        }
        
        List<EntryDO> entries = dao.getEntries(fdo, 0, 10, Folder.SortKey.UID);
        assertEquals(10, entries.size());
        for (int i = 0; i < 10; i++) {
            assertEquals(i+1, entries.get(i).getUid());
        }
    }
    
    @Test
    public void getEntriesSortedBySubject() throws Exception {
        MailboxDO mdo = dao.create("bart");
        FolderDO fdo = new FolderDO(path("INBOX"), mdo);
        fdo = dao.save(fdo);
        
        DBMessageDAO mesDAO = new DBMessageDAO(dbConfig.getDataSource(), 
                dbConfig.getDialect());
        mesDAO.init();
        
        SimpleMessageBuilder smb = new SimpleMessageBuilder();
        smb.setContent(new Content("42"));
        smb.getHeaderBuilder().setFrom("<bart@example.com>")
            .setTo("<lisa@example.com>");

        smb.getHeaderBuilder().setSubject("dddddd");
        Entity m4 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m4.getId());
        
        smb.getHeaderBuilder().setSubject("bbbbbb");
        Entity m2 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m2.getId());
        
        smb.getHeaderBuilder().setSubject("cccccc");
        Entity m3 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m3.getId());
        
        smb.getHeaderBuilder().setSubject("eeeeee");
        Entity m5 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m5.getId());
        
        smb.getHeaderBuilder().setSubject("aaaaaa");
        Entity m1 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m1.getId());
        
        List<EntryDO> entries = dao.getEntries(fdo, 0, 10, Folder.SortKey.SUBJECT);
        assertEquals(5, entries.size());
        assertEquals(m1.getId(), entries.get(0).getMessageId());
        assertEquals(m2.getId(), entries.get(1).getMessageId());
        assertEquals(m3.getId(), entries.get(2).getMessageId());
        assertEquals(m4.getId(), entries.get(3).getMessageId());
        assertEquals(m5.getId(), entries.get(4).getMessageId());
    }
    
    @Test
    public void getEntriesSortedByFrom() throws Exception {
        MailboxDO mdo = dao.create("bart");
        FolderDO fdo = new FolderDO(path("INBOX"), mdo);
        fdo = dao.save(fdo);
        
        DBMessageDAO mesDAO = new DBMessageDAO(dbConfig.getDataSource(), 
                dbConfig.getDialect());
        mesDAO.init();
        
        SimpleMessageBuilder smb = new SimpleMessageBuilder();
        smb.setContent(new Content("42"));
        smb.getHeaderBuilder().setSubject("D'oh")
            .setTo("<lisa@example.com>");

        smb.getHeaderBuilder().setFrom("<dddddd@example.com>");
        Entity m4 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m4.getId());
        
        smb.getHeaderBuilder().setFrom("<bbbbbb@example.com>");
        Entity m2 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m2.getId());
        
        smb.getHeaderBuilder().setFrom("<cccccc@example.com>");
        Entity m3 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m3.getId());
        
        smb.getHeaderBuilder().setFrom("<eeeeee@example.com>");
        Entity m5 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m5.getId());
        
        smb.getHeaderBuilder().setFrom("<aaaaaa@example.com>");
        Entity m1 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m1.getId());
        
        List<EntryDO> entries = dao.getEntries(fdo, 0, 10, Folder.SortKey.FROM);
        assertEquals(5, entries.size());
        assertEquals(m1.getId(), entries.get(0).getMessageId());
        assertEquals(m2.getId(), entries.get(1).getMessageId());
        assertEquals(m3.getId(), entries.get(2).getMessageId());
        assertEquals(m4.getId(), entries.get(3).getMessageId());
        assertEquals(m5.getId(), entries.get(4).getMessageId());
    }

    @Test
    public void getEntriesSortedByDate() throws Exception {
        MailboxDO mdo = dao.create("bart");
        FolderDO fdo = new FolderDO(path("INBOX"), mdo);
        fdo = dao.save(fdo);
        
        DBMessageDAO mesDAO = new DBMessageDAO(dbConfig.getDataSource(), 
                dbConfig.getDialect());
        mesDAO.init();
        
        SimpleMessageBuilder smb = new SimpleMessageBuilder();
        smb.setContent(new Content("42"));
        smb.getHeaderBuilder().setFrom("<bart@example.com>")
            .setTo("<lisa@example.com>")
            .setSubject("D'oh!");

        smb.getHeaderBuilder().setDate("Sun, 14 Jan 2007 13:09:00 +0000");
        Entity m4 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m4.getId());
        
        smb.getHeaderBuilder().setDate("Sun, 12 Jan 2007 13:09:00 +0000");
        Entity m2 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m2.getId());
        
        smb.getHeaderBuilder().setDate("Sun, 13 Jan 2007 13:09:00 +0000");
        Entity m3 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m3.getId());
        
        smb.getHeaderBuilder().setDate("Sun, 15 Jan 2007 13:09:00 +0000");
        Entity m5 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m5.getId());
        
        smb.getHeaderBuilder().setDate("Sun, 11 Jan 2007 13:09:00 +0000");
        Entity m1 = mesDAO.save(smb.build());
        dao.addMessage(fdo, m1.getId());
        
        List<EntryDO> entries = dao.getEntries(fdo, 0, 10, Folder.SortKey.DATE);
        assertEquals(5, entries.size());
        assertEquals(m1.getId(), entries.get(0).getMessageId());
        assertEquals(m2.getId(), entries.get(1).getMessageId());
        assertEquals(m3.getId(), entries.get(2).getMessageId());
        assertEquals(m4.getId(), entries.get(3).getMessageId());
        assertEquals(m5.getId(), entries.get(4).getMessageId());
    }
}
